package com.ys.jsontest;

import android.content.Context;
import android.content.res.AssetManager;

import java.io.InputStream;
import java.security.PrivateKey;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import cfca.sadk.algorithm.common.Mechanism;
import cfca.sadk.algorithm.sm2.SM2PublicKey;
import cfca.sadk.lib.crypto.JCrypto;
import cfca.sadk.lib.crypto.Session;
import cfca.sadk.util.Base64;
import cfca.sadk.util.EnvelopeUtil;
import cfca.sadk.util.KeyUtil;
import cfca.sadk.util.Signature;
import cfca.sadk.x509.certificate.X509Cert;


/**
 * @类名称：GMSignUtil
 * @类描述：国密算法SM2签名工具类
 * @作者：zhanggy
 * @日期：2021年8月11日
 */
public class GMSignUtils {

    /**
     * 国密算法数据加密(SM2公钥加密：长度通常限定为136字节内)
     *
     * @param src
     * @param data
     * @throws Exception
     */
    public static String encryptData(String src, String data) throws Exception {
        String encryptMsg = null;
        GMCertInfo certInfo = getVerifyCertInfo(src);
        try {
            final String deviceName = JCrypto.JSOFT_LIB;
            JCrypto.getInstance().initialize(deviceName, null);
            final Session session = JCrypto.getInstance().openSession(deviceName);
            // 一个或者多个加密证书
            X509Cert[] recvcerts = new X509Cert[]{certInfo.getX509Cert()};
            byte[] base64Bytes = EnvelopeUtil.envelopeMessage(data.getBytes("UTF-8"), Mechanism.SM4_ECB, recvcerts, session);
            encryptMsg = new String(base64Bytes);
        } catch (Exception e) {
            throw new Exception("数据SM2加密失败");
        }
        return encryptMsg;
    }

    /**
     * 国密算法数据加密(SM2公钥加密：长度通常限定为136字节内)
     *
     * @param
     * @param data
     * @throws Exception
     */
    public static String encryptDataStr(String crtStr, String data) throws Exception {
        String encryptMsg = null;
        GMCertInfo certInfo = getVerifyCertInfoStr(crtStr);
        try {
            final String deviceName = JCrypto.JSOFT_LIB;
            JCrypto.getInstance().initialize(deviceName, null);
            final Session session = JCrypto.getInstance().openSession(deviceName);
            // 一个或者多个加密证书
            X509Cert[] recvcerts = new X509Cert[]{certInfo.getX509Cert()};
            byte[] base64Bytes = EnvelopeUtil.envelopeMessage(data.getBytes("UTF-8"), Mechanism.SM4_ECB, recvcerts, session);
            encryptMsg = new String(base64Bytes);
        } catch (Exception e) {
            throw new Exception("数据SM2加密失败");
        }
        return encryptMsg;
    }

    /**
     * 获取支付平台公钥证书
     *
     * @param src
     * @return
     * @throws Exception
     */
    public static GMCertInfo getVerifyCertInfo(String src) throws Exception {
        GMCertInfo certInfo = new GMCertInfo();
        try {
            certInfo.ReadPublicKeyFromX509CertificateSM2(src);
        } catch (Exception e) {
            throw new Exception("银盛平台公钥证书加载失败");
        }

        return certInfo;
    }

    /**
     * 获取支付平台公钥证书
     *
     * @param pubKey
     * @return
     * @throws Exception
     */
    public static GMCertInfo getVerifyCertInfoStr(String pubKey) throws Exception {
        GMCertInfo certInfo = new GMCertInfo();
        try {
            certInfo.ReadPublicKeyFromX509CertificateSM2Str(pubKey);
        } catch (Exception e) {
            throw new Exception("银盛平台公钥证书加载失败");
        }

        return certInfo;
    }

    /**
     * 加载公钥证书
     *
     * @return
     */
    public synchronized static GMCertInfo initValidateCert(byte[] bs, String certId) {
        GMCertInfo certInfo = new GMCertInfo();
        try {
            certInfo.ReadPublicKeyFromX509CertificateSM2(bs);
            return certInfo;
        } catch (Exception e) {
            System.out.println("银盛平台公钥证书加载失败" + e);
        }
        return certInfo;
    }

    /**
     * 国密算法验证签名（报文）
     *
     * @param certInfo
     * @param bcheck
     * @param xmlMsg
     * @throws Exception
     */
    public static boolean verifyMsgSignSM2(GMCertInfo certInfo, byte[] bcheck, byte[] xmlMsg) {
        boolean bFlag;
        try {
            SM2PublicKey pubKey = certInfo.getPubKeySM2();
            final String deviceName = JCrypto.JSOFT_LIB;
            JCrypto.getInstance().initialize(deviceName, null);
            Session session = JCrypto.getInstance().openSession(deviceName);
            final Signature util = new Signature();
            final String signAlg = Mechanism.SM3_SM2;//SM3WithSM2
            // 校验，必须指定签名算法
            bFlag = util.p1VerifyMessage(signAlg, xmlMsg, bcheck, pubKey, session);
        } catch (Exception e) {
            bFlag = false;
            System.out.println("国密验签失败" + e);
        }
        return bFlag;
    }

    /**
     * 获取商户私钥证书
     *
     * @param
     * @return
     * @throws Exception
     */
    public synchronized static GMCertInfo getSignCertInfo(Context context) throws Exception {
        //商户私钥证书
        GMCertInfo certInfo = new GMCertInfo();
        try {
            byte[] priKeySm2 = readFileFromAssets(context,"test.cer");
            certInfo.setPriKey(priKeySm2);
        } catch (Exception e) {
            throw new Exception("商户签名证书加载失败");
        }

        return certInfo;
    }


    public static byte[] readFileFromAssets(Context context, String filename) {
        byte[] buffer = null;
        AssetManager am = context.getAssets();
        try {
            InputStream inputStream = null;
            inputStream = am.open(filename);

            int length = inputStream.available();
            buffer = new byte[length];
            inputStream.read(buffer);
        } catch (Exception exception) {
            exception.printStackTrace();
        }
        return buffer;
    }

    /**
     * 获取商户私钥证书
     *
     * @param priKeyStr
     * @return
     * @throws Exception
     */
    public synchronized static GMCertInfo getSignCertInfoStr(String priKeyStr) throws Exception {
        //商户私钥证书
        GMCertInfo certInfo = new GMCertInfo();
        try {
            byte[] priKeySm2 = Base64.decode(priKeyStr);
            certInfo.setPriKey(priKeySm2);
        } catch (Exception e) {
            throw new Exception("商户签名证书加载失败");
        }

        return certInfo;
    }

    /**
     * 国密算法签名（报文）
     *
     * @param sm2FilePath
     * @param msg
     * @return 返回 256长度base64编码
     * @throws Exception
     */
    public synchronized static String signMsgSM2(Context context,String sm2FilePath, String passWord, String msg) throws Exception {
        GMCertInfo certInfo = getSignCertInfo(context);
        // 返回的是256位
        byte[] signed;
        try {
            final String deviceName = JCrypto.JSOFT_LIB;
            JCrypto.getInstance().initialize(deviceName, null);
            Session session = JCrypto.getInstance().openSession(deviceName);
            PrivateKey priKey = KeyUtil.getPrivateKeyFromSM2(certInfo.getPriKey(), passWord);
//			PrivateKey priKey = KeyUtil.getPrivateKeyFromSM2(certInfo.getPriKey(),"ys123456");
            byte[] sourceData = msg.getBytes();
            Signature util = new Signature();
            String signAlg = Mechanism.SM3_SM2;//SM3WithSM2
            // 签名，必须指定签名算法，返回BASE64签名结果
            signed = util.p1SignMessage(signAlg, sourceData, priKey, session);
        } catch (Exception e) {
            throw new Exception("报文SM2签名失败");
        }
        String checkValue = new String(signed);
        return String.format("%-256s", checkValue);
    }

    /**
     * 国密算法签名（报文）
     *
     * @param
     * @param msg
     * @return 返回 256长度base64编码
     * @throws Exception
     */
    public synchronized static String signMsgSM2Str(String priKeyStr, String passWord, String msg) throws Exception {
        GMCertInfo certInfo = getSignCertInfoStr(priKeyStr);
        // 返回的是256位
        byte[] signed;
        try {
            final String deviceName = JCrypto.JSOFT_LIB;
            JCrypto.getInstance().initialize(deviceName, null);
            Session session = JCrypto.getInstance().openSession(deviceName);
            PrivateKey priKey = KeyUtil.getPrivateKeyFromSM2(certInfo.getPriKey(), passWord);
//			PrivateKey priKey = KeyUtil.getPrivateKeyFromSM2(certInfo.getPriKey(),"ys123456");
            byte[] sourceData = msg.getBytes();
            Signature util = new Signature();
            String signAlg = Mechanism.SM3_SM2;//SM3WithSM2
            // 签名，必须指定签名算法，返回BASE64签名结果
            signed = util.p1SignMessage(signAlg, sourceData, priKey, session);
        } catch (Exception e) {
            throw new Exception("报文SM2签名失败");
        }
        String checkValue = new String(signed);
        return String.format("%-256s", checkValue);
    }

    public static String getSignDataStr1(Map<String, String> map) {
        List<String> keys = new ArrayList<String>(map.keySet());
        Collections.sort(keys);
        StringBuilder sb = new StringBuilder();
        for (String key : keys) {
            if ("sign".equals(key)) {
                continue;
            }
            sb.append(key).append("=");
            sb.append(map.get(key));
            sb.append("&");
        }
        if (sb.length() > 0) {
            sb.setLength(sb.length() - 1);
        }
        return sb.toString();
    }
}
